// ===========================================================================================================================

/** Contains base classes for UI development.
* This namespace only contains UI (view) related elements, and never controller related logic.  The business logic of an
* application should be kept in one or more "controller" scripts (following MVC patterns).
*/
module DreamSpace.System.Platform.UI {

    // =======================================================================================================================

    /** A system module encapsulates the configuration for multiple application projects.
    * Since the system represents the host's global environment, it is a static root (hence 'module') for all applications.
    */

    // =======================================================================================================================

    /** This type serves only as a more meaningful type name place holder (since any UI can be supported, so no requirements
    * are added at this level). */
    export interface IUIObject { }

    export interface IViewType<TView extends UI.View.$Type<UI.IUIObject>> { (...args): TView; $Type: { new (...args): TView; } }

    export declare function View<TUIObject extends IUIObject>(): View.$Type<TUIObject>;
    AppDomain.registerType("View", [DreamSpace, System, Platform, UI]);
    /** A base class for ALL UI based graph objects (HTML, Windows, Mobile, etc.).
    * The base UI type takes one type parameter, which is the base type shared by all UI objects for the UI being implemented.
    */
    export module View {
        export class $Type<TUIObject extends IUIObject> extends Object.$Type implements IGraphItemView {
            // -------------------------------------------------------------------------------------------------------------------

            __graphItem: GraphItem.$Type;

            __uiElement: TUIObject = null;
            __uiElementIndex: number = 0;
            __uiElementParent: IUIObject = null;

            constructor(/*parentView: $Type<TUIObject>*/) {
                //if (!parentView.__graphItem)
                //    throw Exception.error("UIBase()", "The specified parent view object is not associated with a graph item.", parent);
                //if (!this.__graphItem)
                //    throw Exception.error("UIBase()", "Cannot attach to parent view: this view object is not associated with a graph item.", parent);
                super();
                //?? this.visualUpdateHandlers.addListener(UIBase.__updateVisuals);
            }

            // -------------------------------------------------------------------------------------------------------------------

            /** Returns true if this UI graph item exists in a visual tree.
            * Derived types should override and return true or false based on UI implementation. */
            isInVisualTree(): boolean {
                return void 0;
            }

            // -------------------------------------------------------------------------------------------------------------------

            /** Traverses the parent hierarchy to locate the nearest parent UIBase related object with a valid UI element/control. 
            * This function exists to allow skipping non-visual UI objects when building the visual object tree.
            */
            getParentView(): View.$Type<any> {
                var parent = this.__graphItem && this.__graphItem.__parent;
                while (parent && (!parent.__view || !parent.__view.__uiElement)) parent = parent.__parent;
                return parent ? parent.__view : null;
            }

            /** Traverses the parent hierarchy to locate the nearest parent element/control.
            * This function is the same as calling 'getParentUIObject()' to retrieve the underlying element/control.
            * This function exists to allow skipping non-visual UI objects when building the visual object tree.
            */
            getParentElement(): IUIObject {
                var obj = this.getParentView();
                return obj && obj.__uiElement || null;
            }

            onRootChanged(oldRoot: GraphItem.$Type, newRoot: GraphItem.$Type): void { /*virtual*/
            }

            /** Triggered when the graph item is added to a parent item. */
            onAdded(): void { /*virtual*/
                // ... the parent graph item has changed, so the UI must update also ...
                if (host.isClient()) { // (the server has no UI!)
                    var parentView = this.getParentView();
                    if (parentView)
                        parentView.__doOnGraphItemChanged(void 0, void 0, true);
                }
            } 

            /** Triggered when the graph item is removed from a parent item. */
            onRemoved(): void { /*virtual*/
                if (host.isClient()) { // (the server has no UI!)
                    var parentView = this.getParentView();
                    if (parentView)
                        parentView.__doOnGraphItemChanged(void 0, void 0, true);
                }
            }

            /** Called when visual-based properties have updated. */
            onUpdateVisual(): void { } /*virtual*/

            /** Called when a visual-based property has updated. */
            onVisualPropertyUpdated(property: Property<GraphItem.$Type, any>): void { } /*virtual*/

            // -------------------------------------------------------------------------------------------------------------------

            /** Called when the UI object for this graph item needs to be created, or associations updated.
            * When first called, the UI object reference ({UIBase}.__uiObject} doesn't exist yet.  Typically, derived UI types
            * would override this function in order to create the required UI object.  Also, if the UI object already exists, the
            * derived type is ALSO responsible to make sure that the UI object's parent UI object is the SAME as the UI object
            * associated with the parent of this graph item.  If not, the underlying UI element needs to be moved to the new
            * parent (taking the current graph item child index into account for "z-order" of object placement where necessary).
            * Note: If the current graph item is set to a different UI object than the parent graph item, the current child graph
            * item will become severed from the parent, and will become it's own root for a new graph tree.
            * @param {IUIObject} The parent UI object that this graph item should create a UI object for.
            * @param {integer} The sibling index of where to insert the created/updated UI object for this graph item.
            * Note: The index is "best fit possible", so values < 0 == 0 (beginning), and values >= length == length-1 (end).
            */
            onUpdateUIObject(): void { /*virtual*/
            }

            private __doOnGraphItemChanged(parent: IUIObject, siblingIndex?: number, childChanged?: boolean): number {
                if (parent === void 0) parent = this.__uiElementParent;
                if (siblingIndex === void 0) siblingIndex = this.__uiElementIndex;

                var parentOrIndexChanged = this.__uiElementParent !== parent || this.__uiElementIndex !== siblingIndex;
                var updateUIObject = !this.__uiElement || parentOrIndexChanged;

                if (childChanged || updateUIObject) {
                    if (updateUIObject) {
                        this.__uiElementParent = parent;
                        this.__uiElementIndex = siblingIndex;

                        if (this.onUpdateUIObject !== $Type.prototype.onUpdateUIObject)
                            this.onUpdateUIObject();
                    }

                    var gi = this.__graphItem;

                    if (this.__uiElement) {
                        parent = this.__uiElement;
                        if (gi.__children) {
                            for (var i = 0, n = gi.__children.length, childUIIndex = 0; i < n; ++i)
                                if (gi.__children[i].__view)
                                    childUIIndex = gi.__children[i].__view.__doOnGraphItemChanged(parent, childUIIndex);
                        }
                    }
                    else // (this object has no UI, so pass along the index in order to virtually flatten the top most root visual objects of each child tree)
                        if (parent && gi.__children) // (if no parent UI object is given, then the children have nothing to do!!!)
                            for (var i = 0, n = gi.__children.length; i < n; ++i)
                                if (gi.__children[i].__view)
                                    siblingIndex = gi.__children[i].__view.__doOnGraphItemChanged(parent, siblingIndex);
                }

                if (this.__uiElement)
                    ++siblingIndex; // (assume that the existence of a UI object means this is a visual UI object, so increment the index for the next sibling)

                return siblingIndex;
            }

            ///** Attaches this graph item to the specified parent object.
            //* If the object is connected to a visual element, the element will be added to its parent element also.
            //* Note: Override this in derived types that manage visual elements, and make sure to call back to this function.
            //*/
            //??attach(parent: GraphItem.$Type, index?: number): $Type<TUIObject>;
            ///** Attach an element to this UI graph item.
            //* By default, graph items do no need a UI.  The graph system can be viewed as the "controller" (using MVC patterns) of
            //* the application, in which most (if not all) the business logic resides.  If a UI is available, then attaching an element
            //* (DOM element, or other UI type control) to a UI graph item triggers a series of events that attempt to attach all child
            //* graph items as well.  Once attached, the current Child items will automatically create their own 
            //*/
            //??attach<T extends TUIObject>(parent: T, index?: number): $Type<TUIObject>;
            //??attach<T extends TUIObject>(parent: T, index?: number): $Type<TUIObject> { // (extends base "attach(parent: GraphItem.$Type, index?: number): GraphItem;")


            //    //??this.visualIsValid = true; // (note: when updated on child items, prevents any additional visual update timers from triggering overlapping updates)

            //    return this;
            //}

            // -------------------------------------------------------------------------------------------------------------------

            ///** Updates the visual display of all child graph items (@see GraphItem.updateLayout). 
            //* Expected to be overridden by derived types that store their layout/css information in the observable properties. 
            //* This function is called when properties are completely replaced (such as when loading a project), rather than
            //* calling an event for each property set, which can be much slower.
            //* Typically, derived types simply update their CSS, innerHTML, or both, in this function.
            //*/
            //??onRedraw(recursive: boolean = true) {
            //    if (recursive && this.__children)
            //        for (var i = 0; i < this.__children.length; i++)
            //            this.__children[i].onRedraw();
            //}

            // -------------------------------------------------------------------------------------------------------------------

            on(eventName: string, handler: EventHandler, eventMode: EventModes = EventModes.Capture): EventDispatcher.$Type<GraphItem.$Type, EventHandler> { return null; }

            // -------------------------------------------------------------------------------------------------------------------
        }
        //// ... need to extend UIBase to point to the GraphItem prototype instead ...
        //??Utilities.extend($Type, GraphItem.$Type, false); // (replaces the proto with a new one, but copies over the proto props)
        AppDomain.registerClass(View);
    }

    // =======================================================================================================================
} // (end Application.UI)

// =======================================================================================================================
